package com.atlassian.jira.upgrade;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;

import javax.servlet.ServletContext;

import org.apache.log4j.Logger;

import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.bc.license.JiraLicenseService;
import com.atlassian.jira.bean.export.IllegalXMLCharactersException;
import com.atlassian.jira.license.JiraLicenseStore;
import com.atlassian.jira.license.LicenseJohnsonEventRaiser;
import com.atlassian.jira.startup.JiraLauncher;
import com.atlassian.jira.startup.JiraStartupChecklist;
import com.atlassian.jira.util.BuildUtilsInfo;
import com.atlassian.jira.util.I18nHelper;
import com.atlassian.jira.web.ServletContextProvider;
import com.atlassian.jira.web.util.ExternalLinkUtil;
import com.atlassian.johnson.JohnsonEventContainer;
import com.atlassian.johnson.event.Event;
import com.atlassian.johnson.event.EventLevel;
import com.atlassian.johnson.event.EventType;

public class UpgradeLauncher
  implements JiraLauncher
{
  private static final Logger log = Logger.getLogger(UpgradeLauncher.class);
  private static final String NO_UPGRADE_MESSAGE = "No upgrade is being performed due to detected inconsistencies.";
  private static final int V1 = 1;
  private static final String LS = System.getProperty("line.separator");

  public void start()
  {
    try
    {
      checkIfUpgradeNeeded(ServletContextProvider.getServletContext());
    }
    catch (RuntimeException rte)
    {
      log.fatal(new StringBuilder().append("A RuntimeException occurred during UpgradeLauncher servlet context initialisation - ").append(rte.getMessage()).append(".").toString(), rte);
      throw rte;
    }
    catch (Error error)
    {
      log.fatal(new StringBuilder().append("An Error occurred during UpgradeLauncher servlet context initialisation - ").append(error.getMessage()).append(".").toString(), error);
      throw error;
    }
  }

  public void stop()
  {
  }

  public static void checkIfUpgradeNeeded(ServletContext servletContext)
  {
    ensureLicenseNotCorrupted();
    if (JiraStartupChecklist.startupOK())
    {
      JohnsonEventContainer eventContainer = JohnsonEventContainer.get(servletContext);

      if (checkLicenseIsValid(servletContext))
      {
        Event upgradingEvent = new Event(EventType.get("upgrade"), "JIRA is currently being upgraded", EventLevel.get("warning"));

        eventContainer.addEvent(upgradingEvent);
        try {
          UpgradeManager manager = ComponentManager.getInstance().getUpgradeManager();
          File tempDir = (File)servletContext.getAttribute("javax.servlet.context.tempdir");
          Collection errors;
          try {
            errors = manager.doUpgradeIfNeededAndAllowed(tempDir.getAbsolutePath());
          }
          catch (IllegalXMLCharactersException ie)
          {
            log.error("Illegal XML characters detected while exporting before upgrade.");

            Event errorEvent = new Event(EventType.get("export-illegal-xml"), "Illegal XML characters in data prevent successful export before the upgrade.", ie.getMessage(), EventLevel.get("error"));

            eventContainer.addEvent(errorEvent);

            eventContainer.removeEvent(upgradingEvent); return;
          }
          addEventsForErrors(eventContainer, errors);
        }
        catch (Throwable e)
        {
          log.error(new StringBuilder().append("Exception whilst trying to upgrade: ").append(e.getMessage()).toString(), e);

          Event errorEvent = new Event(EventType.get("upgrade"), "An error occurred performing JIRA upgrade", e.getMessage(), EventLevel.get("error"));

          eventContainer.addEvent(errorEvent);
        }
        finally
        {
          eventContainer.removeEvent(upgradingEvent);
        }
      }
      else
      {
        log.error(constructErrorMessage(eventContainer));
      }
    }
    else
    {
      log.fatal("Skipping, JIRA is locked.");
    }
  }

  private static void ensureLicenseNotCorrupted()
  {
    try
    {
      getJiraLicenseService().getLicense();
    }
    catch (Exception e)
    {
      log.error(String.format("The JIRA license stored in this instance's database is corrupted. The license entry will be removed and JIRA will act as if there was no valid license set. The corrupted license string: '%s'", new Object[] { getJiraLicenseStore().retrieve() }));

      getJiraLicenseStore().remove();
    }
  }

  private static boolean checkLicenseIsValid(ServletContext servletContext)
  {
    /*boolean invalid = false;
    JiraLicenseService licenseService = getJiraLicenseService();

    LicenseDetails licenseDetails = licenseService.getLicense();
    if (licenseDetails.isLicenseSet())
    {
      LicenseJohnsonEventRaiser licenseJohnsonEventRaiser = getLicenseJohnsonEventRaiser();

      invalid |= licenseJohnsonEventRaiser.checkLicenseIsTooOldForBuild(servletContext, licenseDetails);

      invalid |= licenseJohnsonEventRaiser.checkLicenseIsInvalid(servletContext, licenseDetails);
    }
    return !invalid;*/
	  return true;
  }

  private static void addEventsForErrors(JohnsonEventContainer cont, Collection<String> errors)
  {
    for (String exception : errors)
    {
      Event errorEvent = new Event(EventType.get("upgrade"), "An error occurred performing JIRA upgrade", exception, EventLevel.get("error"));

      if (cont != null)
      {
        cont.addEvent(errorEvent);
      }
    }
  }

  private static String constructErrorMessage(JohnsonEventContainer cont)
  {
    StringBuilder errMsg = new StringBuilder("No upgrade is being performed due to detected inconsistencies.").append(" ");

    JiraLicenseService licenseService = getJiraLicenseService();
    JiraLicenseService.ValidationResult validationResult = licenseService.validate(getI18nHelper(), licenseService.getLicense().getLicenseString());
    Iterator i$;
    if (validationResult.getLicenseVersion() == 1)
    {
      BuildUtilsInfo buildUtilsInfo = getBuildUtilsInfo();
      ExternalLinkUtil externalLinkUtil = getExternalLinkUtil();

      String serverId = licenseService.getServerId();
      String upgradeLink = externalLinkUtil.getProperty("external.link.jira.upgrade.lic", Arrays.asList(new String[] { buildUtilsInfo.getVersion(), buildUtilsInfo.getCurrentBuildNumber(), "enterprise", serverId, String.valueOf(validationResult.getTotalUserCount()), String.valueOf(validationResult.getActiveUserCount()) }));
      String evaluationLink = externalLinkUtil.getProperty("external.link.jira.license.view", Arrays.asList(new String[] { buildUtilsInfo.getVersion(), buildUtilsInfo.getCurrentBuildNumber(), "enterprise", serverId }));

      errMsg.append(LS).append(LS).append("The current version of your license (v1) is incompatible with JIRA ").append(buildUtilsInfo.getVersion()).append(".").append(LS).append("You will need to upgrade your license or generate an evaluation license.").append(LS).append(LS).append("To upgrade your license visit : ").append(upgradeLink).append(LS).append("To generate an evaluation license visit : ").append(evaluationLink).append(LS).append(LS).append("Please follow the instructions that JIRA is presenting in your web browser.").append(LS);
    }
    else
    {
      for (i$ = cont.getEvents().iterator(); i$.hasNext(); ) { Object element = i$.next();

        Event errEvent = (Event)element;
        errMsg.append(errEvent.getDesc()).append(LS);
      }
    }
    return errMsg.toString();
  }

  private static JiraLicenseService getJiraLicenseService()
  {
    return (JiraLicenseService)ComponentManager.getComponent(JiraLicenseService.class);
  }

  private static JiraLicenseStore getJiraLicenseStore()
  {
    return (JiraLicenseStore)ComponentManager.getComponent(JiraLicenseStore.class);
  }

  private static I18nHelper getI18nHelper()
  {
    return ((I18nHelper.BeanFactory)ComponentManager.getComponent(I18nHelper.BeanFactory.class)).getInstance(Locale.getDefault());
  }

  private static LicenseJohnsonEventRaiser getLicenseJohnsonEventRaiser()
  {
    return (LicenseJohnsonEventRaiser)ComponentManager.getComponent(LicenseJohnsonEventRaiser.class);
  }

  private static BuildUtilsInfo getBuildUtilsInfo()
  {
    return (BuildUtilsInfo)ComponentManager.getComponent(BuildUtilsInfo.class);
  }

  private static ExternalLinkUtil getExternalLinkUtil()
  {
    return (ExternalLinkUtil)ComponentManager.getComponent(ExternalLinkUtil.class);
  }
}